<?php
declare(strict_types=1);

namespace Hyperfx\Utils;

use Hidehalo\Nanoid\Client;
use Hyperf\Grpc\StatusCode;
use Hyperf\GrpcServer\Exception\GrpcException;
use Hyperf\Redis\Redis;
use Hyperf\Utils\ApplicationContext;
use Lysice\HyperfRedisLock\LockTimeoutException;
use Lysice\HyperfRedisLock\RedisLock;

class GenerateId
{
    public static function nanoId(int $size = 21): string {
        $client = new Client();
        return  $client->generateId($size);
    }

    public static function clearGenerateAutoId(string $collection): void
    {
        $container = ApplicationContext::getContainer();

        $redis = $container->get(Redis::class);

        if (empty($collection)) {
            throw new GrpcException('collection 不能为空', StatusCode::NOT_FOUND);
        }

        $redisKey = 'auto_increment_id#' . $collection;
        $redisLockKey = $redisKey . '#lock';

        $redis->del($redisKey);
        $redis->del($redisLockKey);

    }
    public static function generateAutoId(string $collection, int $high, int $min = 1, callable $callback = null): int
    {
        $container = ApplicationContext::getContainer();

        $redis = $container->get(Redis::class);

        if (empty($collection)) {
            throw new GrpcException('collection 不能为空', StatusCode::NOT_FOUND);
        }

        $finalHigh = $high;//需要增加的数，在重置的时候需要去数据库获得最大值然后加上high
        $redisKey = 'auto_increment_id#' . $collection;
        $redisLockKey = $redisKey . '#lock';
        if (!$redis->exists($redisKey)) {
            $lock = new RedisLock($redis, $redisLockKey, 5);
            try {
                $maxDefaultAutoId = $lock->block(1, function () use ($callback, $min, $high, $redis, $redisKey) {
                    if ($redis->exists($redisKey)) {
                        return -1;
                    }
                    $callbackId = 0;
                    if (is_callable($callback)) {
                        $callbackId = $callback();
                    }

                    $newId = max($min, $callbackId) + $high;

                    if (!$redis->set($redisKey, $newId)) {
                        throw new GrpcException('生成失败，请稍后再试', StatusCode::ABORTED);
                    }

                    return $newId;

                });
                if ($maxDefaultAutoId > 0) {
                    return $maxDefaultAutoId;
                }
            } catch (LockTimeoutException $e) {
                throw new GrpcException('生成失败，请稍后再试', StatusCode::ABORTED);
            }
        }
        $newId = $redis->incrBy($redisKey, $finalHigh);

        if (!$newId) {
            throw new GrpcException('生成失败，请稍后再试', StatusCode::ABORTED);
        }

        if ($newId  < $min) {
            $redis->del($redisKey);
            throw new GrpcException('生成失败，请稍后再试', StatusCode::ABORTED);
        }

        return $newId;

    }
}
